package net.sf.rose.jdbc.bean;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import net.sf.rose.boot.BootStrap;
import net.sf.rose.boot.bean.DataTypeBean;
import net.sf.rose.jdbc.KeyGenerator;
import net.sf.rose.jdbc.bean.annotation.Column;
import net.sf.rose.jdbc.bean.annotation.Table;
import net.sf.rose.jdbc.bean.annotation.TableData;
import net.sf.rose.jdbc.bean.annotation.Type;
import net.sf.rose.jdbc.bean.annotation.View;
import net.sf.rose.jdbc.bean.annotation.Views;
import net.sf.rose.jdbc.service.Service;
import net.sf.rose.util.StringUtil;

/**
 * 类说明：数据库表定义元数据类
 * 
 * @author 作者 fgg047 E-mail: 9110530@qq.com
 * @version 创建时间：2018-12-09
 */
@Table(name = "ADMIN_TABLE", description = "数据库表结构定义说明")
public class TableBean implements Comparable<TableBean> {

	private static Logger LOG = Logger.getLogger(TableBean.class);

	@Column(name = "TABLE_ID", pk = true, type = Type.定长文本, description = "编号", canNull = false, size = 32, policy = "UUID")
	private String tableID = null;

	/** 数据库名 */
	@Column(name = "DATABASE_NAME", type = Type.变长文本, description = "数据库名", size = 100)
	private String databaseName = null;

	@Column(name = "MODULE_NAME", type = Type.变长文本, description = "模块名称", size = 100)
	private String moduleName = null;

	/** 表名称 */
	@Column(name = "TABLE_NAME", type = Type.变长文本, description = "表名称", size = 100)
	private String tableName = null;

	/** 表说明 */
	@Column(name = "DESCRIPTION", type = Type.变长文本, description = "表说明", size = 250)
	private String description = null;

	/** 实体类的类名 */
	@Column(name = "CLASS_NAME", type = Type.变长文本, description = "实体类的类名", size = 150)
	private String className = null;

	/** 是否是视图；1：是；2：否 */
	@Column(name = "IS_VIEW", type = Type.数字整型, description = "是否是视图")
	private int view = 2;

	/** 视图查询SQL */
	@Column(name = "VIEW_SQL", type = Type.超长文本, description = "视图查询SQL")
	private String viewSql = null;

	/** 字段列表 */
	private List<FieldBean> fields = new ArrayList<>();

	/** 表主键 */
	private transient FieldBean pkField = null;

	/** 字段名与字段定义的Map键值对 */
	private transient Map<String, FieldBean> dbNameFieldMap = null;

	/** 字段属性名与字段定义的Map键值对 */
	private transient Map<String, FieldBean> fieldNameFieldMap = null;

	/**
	 * 添加字段定义
	 * 
	 * @param field 字段定义
	 */
	public void addField(FieldBean field) {
		this.fields.add(field);
	}

	/**
	 * 更新字段定义
	 * 
	 * @param field 字段定义
	 */
	public void updateField(FieldBean field) {
		if (this.fields != null) {
			FieldBean bean = this.getFieldBeanByPropertyName(field.getFieldName());
			if (bean != null) {
				bean.update(field);
			}
		}
	}

	/**
	 * 删除字段定义
	 * 
	 * @param field 字段定义
	 */
	public void removeField(FieldBean field) {
		if (this.fields != null) {
			String name = field.getFieldName();
			for (int i = 0; i < this.fields.size(); i++) {
				FieldBean f = this.fields.get(i);
				if (f.getFieldName().equals(name)) {
					this.fields.remove(i);
				}
			}
		}
	}

	/**
	 * 根据属性名称返回字段定义类
	 * 
	 * @param propertyName - 属性名称
	 * @return 数据库表字段定义元数据类
	 */
	public FieldBean getFieldBeanByPropertyName(String propertyName) {
		for (FieldBean f : this.fields) {
			if (f.getPropertyName().equals(propertyName)) {
				return f;
			}
		}
		return null;
	}

	/**
	 * 根据字段名称返回字段定义类
	 * 
	 * @param fieldName 字段名称
	 * @return 数据库表字段定义元数据类
	 */
	public FieldBean getFieldBeanByFieldName(String fieldName) {
		for (FieldBean f : this.fields) {
			if (f.getFieldName().equalsIgnoreCase(fieldName)) {
				return f;
			}
		}
		return null;
	}

	/**
	 * 返回表的主键字段定义类
	 * 
	 * @return 数据库表主键字段定义元数据类
	 */
	public FieldBean getPkField() {
		return pkField;
	}

	/**
	 * 设置表的主键字段定义类
	 * 
	 * @param keyField - 主键字段定义类
	 */
	public void setPkField(FieldBean keyField) {
		this.pkField = keyField;
	}

	/**
	 * 返回表的字段名与字段定义的Map键值对
	 * 
	 * @return 字段名与字段定义的Map键值对
	 */
	public Map<String, FieldBean> getFieldNameFieldMap() {
		return dbNameFieldMap;
	}

	/**
	 * 返回表的字段属性名与字段定义的Map键值对
	 * 
	 * @return 字段属性名与字段定义的Map键值对
	 */
	public Map<String, FieldBean> getPropertyNameFieldMap() {
		return fieldNameFieldMap;
	}

	/**
	 * 获取字段名与字段定义的Map键值对
	 * 
	 * @return 字段名与字段定义的Map键值对
	 */
	public Map<String, FieldBean> getDbNameFieldMap() {
		return dbNameFieldMap;
	}

	/**
	 * 设置字段名与字段定义的Map键值对
	 * 
	 * @param dbNameFieldMap 字段名与字段定义的Map键值对
	 */
	public void setDbNameFieldMap(Map<String, FieldBean> dbNameFieldMap) {
		this.dbNameFieldMap = dbNameFieldMap;
	}

	/**
	 * 设置字段属性名与字段定义的Map键值对
	 * 
	 * @param fieldNameFieldMap 字段属性名与字段定义的Map键值对
	 */
	public void setFieldNameFieldMap(Map<String, FieldBean> fieldNameFieldMap) {
		this.fieldNameFieldMap = fieldNameFieldMap;
	}

	/**
	 * 生成的主键
	 * 
	 * @return 生成的主键
	 */
	public Object uuid() {
		if (pkField != null && pkField.getBeanKeyGenerator() != null) {
			return pkField.getBeanKeyGenerator().uuid();
		}
		return null;
	}

	/**
	 * 判断是否是同一个数据库表名
	 * 
	 * @param bean 待比较的表定义
	 * @return 是否是同一个
	 */
	public boolean equals(TableBean bean) {
		return this.tableName.equals(bean.getTableName()) ? true : false;
	}

	/**
	 * 初始化keyField、dbNameFieldMap和fieldNameFieldMap
	 */
	public void initialFieldCache() {
		// 字段名与字段定义的Map键值对
		dbNameFieldMap = new HashMap<>();
		// 字段属性名与字段定义的Map键值对
		fieldNameFieldMap = new HashMap<>();
		for (FieldBean field : fields) {
			if (field.isPk()) {
				this.setPkField(field);
			}
			dbNameFieldMap.put(field.getFieldName(), field);
			fieldNameFieldMap.put(field.getPropertyName(), field);
		}
	}

	/**
	 * 功能:返回此数据库实体类的元数据类
	 * 
	 * @param clazz 数据库实体类
	 * @param table 数据库表定义
	 */
	public void initialTable(Class<?> clazz, Table table) {
		// 取类名
		this.className = clazz.getName();
		// 取本类对应的数据库表名称，如果注解没有提供就使用类名称
		String database = table.database();
		database = database.equals("") ? null : database;
		database = database == null ? Service.getDefaultDatabaseName() : database;
		this.setDatabaseName(database);
		this.setTableName(table.name());
		this.setDescription(table.description());
		this.setModuleName(table.module());
		// 如果没有设置所属模块，直接从实体类中获取
		if (StringUtil.isEmpty(this.moduleName)) {
			int index = this.className.indexOf(".bean.");
			if (index != -1) {
				String str = this.className.substring(index + ".bean.".length());
				index = str.lastIndexOf('.');
				if (index != -1) {
					str = str.substring(0, index);
					this.setModuleName(str);
				}
			}
		}
		// 根据数据库类型取数据类型对应关系
		Service service = new Service(databaseName);
		Map<String, DataTypeBean> types = service.getDataType();
		// 通过解析SQL语句的查询结果字段部分来获取所有的字段定义
		// 循环获取类属性注解
		for (Field f : clazz.getDeclaredFields()) {
			addField(this, f, types, false);
		}
		Class<?> parent = clazz.getSuperclass();
		// 如果父类存在
		while (!parent.getName().equals("java.lang.Object")) {
			// 如果父类是数据库表结构映射类
			TableData t = parent.getAnnotation(TableData.class);
			if (t != null) {
				boolean noPresist = false;
				// 如果父类是表或视图映射类，则不需要持久化
				if (parent.getAnnotation(Table.class) != null || parent.getAnnotation(Views.class) != null
						|| parent.getAnnotation(View.class) != null) {
					noPresist = true;
				}
				// 循环获取类属性注解
				for (Field f : parent.getDeclaredFields()) {
					addField(this, f, types, noPresist);
				}
				parent = parent.getSuperclass();
			} else {
				break;
			}
		}
		// 初始化主键、字段名与字段定义的Map键值对,字段属性名与字段定义的Map键值对
		initialFieldCache();
		LOG.info("类" + clazz.getName() + "元数据读取完成...");
	}

	/**
	 * 功能:初始化类定义中的视图部分
	 * 
	 * @param clazz 数据库实体类
	 * @param view  视图定义
	 */
	public void initialView(Class<?> clazz, View view) {
		// 取类名
		this.className = clazz.getName();
		// 取本类对应的数据库表名称，如果注解没有提供就使用类名称
		String database = view.database();
		database = database.equals("") ? null : database;
		database = database == null ? Service.getDefaultDatabaseName() : database;
		this.setDatabaseName(database);
		this.setTableName(view.name());
		this.setDescription(view.description());
		this.setModuleName(view.module());
		// 如果没有设置所属模块，直接从实体类中获取
		if (StringUtil.isEmpty(this.moduleName)) {
			int index = this.className.indexOf(".bean.");
			if (index != -1) {
				String str = this.className.substring(index + ".bean.".length());
				index = str.lastIndexOf('.');
				if (index != -1) {
					str = str.substring(0, index);
					this.setModuleName(str);
				}
			}
		}
		// 根据数据库类型取数据类型对应关系
		Service service = new Service(databaseName);
		Map<String, DataTypeBean> types = service.getDataType();
		// 通过解析SQL语句的查询结果字段部分来获取所有的字段定义
		// 循环获取类属性注解
		for (Field f : clazz.getDeclaredFields()) {
			addField(this, f, types, false);
		}
		Class<?> parent = clazz.getSuperclass();
		// 如果父类存在
		while (!parent.getName().equals("java.lang.Object")) {
			// 如果父类是数据库表结构映射类
			TableData t = parent.getAnnotation(TableData.class);
			if (t != null) {
				boolean noPresist = false;
				// 如果父类是表或视图映射类，则不需要持久化
				if (parent.getAnnotation(Table.class) != null || parent.getAnnotation(Views.class) != null
						|| parent.getAnnotation(View.class) != null) {
					noPresist = true;
				}
				// 循环获取类属性注解
				for (Field f : parent.getDeclaredFields()) {
					addField(this, f, types, noPresist);
				}
				parent = parent.getSuperclass();
			} else {
				break;
			}
		}
		this.setView(1);
		LOG.info("视图" + clazz.getName() + "元数据读取完成...");
	}

	/**
	 * 过滤保留视图查询结果中的字段定义
	 * 
	 * @param list 初始化所有视图字段定义
	 */
	public void initialViewField(List<String> list) {
		Map<String, FieldBean> map = new HashMap<>();
		for (FieldBean bean : fields) {
			map.put(bean.getFieldName().toUpperCase(), bean);
		}
		List<FieldBean> l = new ArrayList<>();
		for (String name : list) {
			FieldBean bean = map.get(name.toUpperCase());
			if (bean != null) {
				l.add(bean);
			}
		}
		fields.clear();
		fields = l;
		this.initialFieldCache();
	}

	/**
	 * 读取字段的定义
	 * 
	 * @param table     表定义
	 * @param field     字段定义
	 * @param types     字段类型
	 * @param noPresist 是否需要持久化
	 */
	private void addField(TableBean table, Field field, Map<String, DataTypeBean> types, boolean noPresist) {
		FieldBean bean = new FieldBean();
		// 如果属性使用transient修饰符表示不需要持久化保存
		if (Modifier.isTransient(field.getModifiers())) {
			bean.setPresist(-1);
		}
		// 如果指定不需要持久化
		if (noPresist) {
			bean.setPresist(-1);
		}
		// 如果有属性注解
		Column column = field.getAnnotation(Column.class);
		if (column != null) {
			// 数据库字段名
			String fieldName = column.name();
			// 如果数据库字段名没有设置就使用类的属性名
			fieldName = fieldName.equals("") ? field.getName() : fieldName;
			// 如果数据库是Oracle，那么字段全部大写
			if (BootStrap.getBootBean().getDatabaseBean(databaseName).isOracle()) {
				fieldName = fieldName.toUpperCase();
			}
			bean.setFieldName(fieldName);
			// 类的属性名
			bean.setPropertyName(field.getName());
			// 字段说明
			if (!column.description().equals("")) {
				bean.setDescription(column.description());
			}
			// 长度
			if (column.size() != 0) {
				bean.setSize(column.size());
			}
			// 小数长度
			if (column.scale() != 0) {
				bean.setScale(column.scale());
			}
			// 允许空值
			if (column.canNull()) {
				bean.setCanNull(1);
			}
			// 默认值
			if (!column.defaultValue().equals("")) {
				bean.setDefaultValue(column.defaultValue());
			}
			// 生成策略
			if (!column.policy().equals("")) {
				bean.setPolicy(column.policy());
			}
			// 数据类型
			Type dbType = column.type();
			DataTypeBean t = types.get(dbType.toString());
			bean.setType(t.getName()); // 数据类型
			bean.setDbType(t.getDbType()); // 数据库数据类型
			bean.setJavaType(t.getJavaType());// Java数据类型
			// 如果需要持久化、而且是否主键
			if (!noPresist && column.pk()) {
				bean.setPk(1);
				if (bean.getPolicy() != null) {
					KeyGenerator kg = new KeyGenerator();
					kg.setkeyGenerateString(bean.getPolicy());
					bean.setBeanKeyGenerator(kg);
				}
			}
			table.addField(bean);
		}
	}

	/**
	 * 获取数据库名
	 * 
	 * @return 数据库名
	 */
	public String getDatabaseName() {
		return databaseName;
	}

	/**
	 * 设置数据库名
	 * 
	 * @param databaseName 数据库名
	 */
	public void setDatabaseName(String databaseName) {
		this.databaseName = databaseName;
	}

	/**
	 * 获取实体类名称
	 * 
	 * @return 实体类名称
	 */
	public String getClassName() {
		return className;
	}

	/**
	 * 设置实体类名称
	 * 
	 * @param className 实体类名称
	 */
	public void setClassName(String className) {
		this.className = className;
	}

	/**
	 * 获取模块名称
	 * 
	 * @return 模块名称
	 */
	public String getModuleName() {
		return moduleName;
	}

	/**
	 * 设置模块名称
	 * 
	 * @param moduleName 模块名称
	 */
	public void setModuleName(String moduleName) {
		this.moduleName = moduleName;
	}

	/**
	 * 获取数据库表名
	 * 
	 * @return 数据库表名
	 */
	public String getTableName() {
		return tableName;
	}

	/**
	 * 设置数据库表名
	 * 
	 * @param tableName 数据库表名
	 */
	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	/**
	 * 获取类描述
	 * 
	 * @return 类描述
	 */
	public String getDescription() {
		return description;
	}

	/**
	 * 设置类描述
	 * 
	 * @param description 类描述
	 */
	public void setDescription(String description) {
		this.description = description;
	}

	/**
	 * 获取所有的字段定义
	 * 
	 * @return 所有的字段定义
	 */
	public List<FieldBean> getFields() {
		return fields;
	}

	/**
	 * 设置所有的字段定义
	 * 
	 * @param fields 所有的字段定义
	 */
	public void setFields(List<FieldBean> fields) {
		this.fields = fields;
	}

	/**
	 * 获取数据库查询视图SQL
	 * 
	 * @return 数据库查询视图SQL
	 */
	public String getViewSql() {
		return viewSql;
	}

	/**
	 * 设置数据库查询视图SQL
	 * 
	 * @param viewSql 数据库查询视图SQL
	 */
	public void setViewSql(String viewSql) {
		this.viewSql = viewSql;
	}

	public String getTableID() {
		return tableID;
	}

	public void setTableID(String tableID) {
		this.tableID = tableID;
	}

	public int getView() {
		return view;
	}

	public void setView(int view) {
		this.view = view;
	}

	public boolean isView() {
		return this.view == 1;
	}

	// 重写Comparable接口的compareTo方法，
	@Override
	public int compareTo(TableBean o) {
		return this.tableName.compareToIgnoreCase(o.tableName);
	}

}
